﻿using System;

using System.Collections.Generic; 

namespace NORM
{
    public class CacheProperties : IDisposable
    {
        private static object _object = new object();
        public static CacheProperties _cacheinstance = null;

        public static CacheProperties cacheInstance
        {
            get
            {
                if (_cacheinstance == null)
                {
                    lock (_object)
                    {
                        if (_cacheinstance == null)
                        {
                            _cacheinstance = new CacheProperties();
                        }
                    }
                }
                return _cacheinstance;
            }
        }

        private int count = 10;
        private double expiry = 10 * 1000 * 60;
        private object locked = new object();

        private System.Reflection.BindingFlags _bindingAttr = System.Reflection.BindingFlags.Default;
        public System.Reflection.BindingFlags bindingAttr
        {
            set
            {
                _cacheinstance = new CacheProperties();
                _bindingAttr = value;
            }
            get { return _bindingAttr; }
        }

        Dictionary<string, CacheItemValue> _dis = new Dictionary<string, CacheItemValue>();

        private CacheProperties()
        {
            _dis = new Dictionary<string, CacheItemValue>();
        }

        private void cacheItemCountControl()
        {
            if (_dis.Count > count - 1)
            {
                string clearItemKey = string.Empty;
                foreach (string key in _dis.Keys)
                {
                    clearItemKey = key;
                    break;
                }
                if (!string.IsNullOrEmpty(clearItemKey))
                    _dis.Remove(clearItemKey);
            }
        }

        private void cacheItemExpiryControl(CacheItemValue item)
        {
            TimeSpan space_timeSpan = DateTime.Now.Subtract(item.CreateTime).Duration();
            double space_milliseconds = space_timeSpan.TotalMilliseconds;
            if (space_milliseconds > expiry)
            {
                Clear(item.TypeName);
            }
        }

        public System.Reflection.PropertyInfo[] GetProperties(Type type)
        {
            lock (locked)
            {
                System.Reflection.PropertyInfo[] result = null;
                if (!_dis.ContainsKey(type.FullName))
                {
                    CacheItemValue item = new CacheItemValue();
                    item.Type = type;
                    item.Name = type.Name;
                    item.TypeName = type.FullName;
                    item.Properties = _bindingAttr == System.Reflection.BindingFlags.Default ? type.GetProperties() : type.GetProperties(_bindingAttr);
                    result = item.Properties;
                    cacheItemCountControl();
                    _dis.Add(item.TypeName, item);
                }
                else
                {
                    CacheItemValue item = new CacheItemValue();
                    if (_dis.TryGetValue(type.FullName, out item))
                    {
                        result = item.Properties;
                    }
                    cacheItemExpiryControl(item);
                }
                return result;
            }
        }

        public System.Reflection.PropertyInfo GetProperty(Type type, string name)
        {           
            lock (locked)
            {
                System.Reflection.PropertyInfo result = null;
                if (!_dis.ContainsKey(type.FullName))
                {
                    CacheItemValue item = new CacheItemValue();
                    item.Type = type;
                    item.Name = type.Name;
                    item.TypeName = type.FullName;
                    item.Properties = _bindingAttr == System.Reflection.BindingFlags.Default ? type.GetProperties() : type.GetProperties(_bindingAttr);
                    result = item.GetProperty(name);
                    cacheItemCountControl();
                    _dis.Add(item.TypeName, item);
                }
                else
                {
                    CacheItemValue item = new CacheItemValue();
                    if (_dis.TryGetValue(type.FullName, out item))
                    {
                        result = item.GetProperty(name);
                    }
                    cacheItemExpiryControl(item);
                }
                return result;
            }           
        }

        public void Clear()
        {
            _dis.Clear();
            _dis = new Dictionary<string, CacheItemValue>();
        }

        public void Clear(string key)
        {
            _dis.Remove(key);
        }

        public void Dispose()
        {
            Clear();
            GC.Collect();
        }

    }

    public class CacheProperties<T> : IDisposable
    {
        private static object _object = new object();
        public static CacheProperties<T> _cacheinstance = null;

        public static CacheProperties<T> cacheInstance
        {
            get
            {
                if (_cacheinstance == null)
                {
                    lock (_object)
                    {
                        if (_cacheinstance == null)
                        {
                            _cacheinstance = new CacheProperties<T>();
                        }
                    }
                }
                return _cacheinstance;
            }
        }

        private int count = 10;
        private long expiry = 10 * 1000 * 60;
        private object locked = new object();

        private System.Reflection.BindingFlags _bindingAttr = System.Reflection.BindingFlags.Default;
        public System.Reflection.BindingFlags bindingAttr
        {
            set
            {
                _cacheinstance = new CacheProperties<T>();
                _bindingAttr = value;
            }
            get { return _bindingAttr; }
        }

        Dictionary<string, CacheItemValue> _dis = new Dictionary<string, CacheItemValue>();

        private CacheProperties()
        {
            _dis = new Dictionary<string, CacheItemValue>();
        }

        private void cacheItemCountControl()
        {
            if (_dis.Count > count - 1)
            {
                string clearItemKey = string.Empty;
                foreach (string key in _dis.Keys)
                {
                    clearItemKey = key;
                    break;
                }
                if (!string.IsNullOrEmpty(clearItemKey))
                    _dis.Remove(clearItemKey);
            }
        }

        private void cacheItemExpiryControl(CacheItemValue item)
        {
            TimeSpan space_timeSpan = DateTime.Now.Subtract(item.CreateTime).Duration();
            double space_milliseconds = space_timeSpan.TotalMilliseconds;
            if (space_milliseconds > expiry)
            {
                Clear(item.TypeName);
            }
        }

        public System.Reflection.PropertyInfo[] GetProperties()
        {
            lock (locked)
            {
                Type type = typeof(T);
                System.Reflection.PropertyInfo[] result = null;
                if (!_dis.ContainsKey(type.FullName))
                {
                    CacheItemValue item = new CacheItemValue();
                    item.Type = type;
                    item.Name = type.Name;
                    item.TypeName = type.FullName;
                    item.Properties = _bindingAttr == System.Reflection.BindingFlags.Default ? type.GetProperties() : type.GetProperties(_bindingAttr);
                    result = item.Properties;
                    cacheItemCountControl();
                    _dis.Add(item.TypeName, item);
                }
                else
                {
                    CacheItemValue item = new CacheItemValue();
                    if (_dis.TryGetValue(type.FullName, out item))
                    {
                        result = item.Properties;
                    }
                    cacheItemExpiryControl(item);
                }
                return result;
            }
        }

        public System.Reflection.PropertyInfo GetProperty(string name)
        {
            lock (locked)
            {
                Type type = typeof(T);
                System.Reflection.PropertyInfo result = null;
                if (!_dis.ContainsKey(type.FullName))
                {
                    CacheItemValue item = new CacheItemValue();
                    item.Type = type;
                    item.Name = type.Name;
                    item.TypeName = type.FullName;
                    item.Properties = type.GetProperties();
                    result = item.GetProperty(name);
                    cacheItemCountControl();
                    _dis.Add(item.TypeName, item);
                }
                else
                {
                    CacheItemValue item = new CacheItemValue();
                    if (_dis.TryGetValue(type.FullName, out item))
                    {
                        result = item.GetProperty(name);
                    }
                    cacheItemExpiryControl(item);
                }
                return result;
            }
        }

        public void Clear()
        {
            _dis.Clear();
            _dis = new Dictionary<string, CacheItemValue>();
        }

        public void Clear(string key)
        {
            _dis.Remove(key);
        }

        public void Dispose()
        {
            Clear();
            GC.Collect();
        }
    }

    internal class CacheItemValue
    {
        public System.Reflection.PropertyInfo[] Properties { get; set; }
        public string Name { get; set; }
        public Type Type { get; set; }
        public String TypeName { get; set; }
        public DateTime CreateTime { get; set; }
        public DateTime ExpireTime { get; set; }
        public int Index { get; set; }

        public CacheItemValue()
        {
            Index = 0;
            CreateTime = DateTime.Now;
        }

        public System.Reflection.PropertyInfo GetProperty(string name)
        {
            System.Reflection.PropertyInfo result = null;
            if (this.Properties != null)
            {
                foreach (System.Reflection.PropertyInfo pi
                    in this.Properties)
                {
                    if (pi.Name.Equals(name))
                    {
                        result = pi;
                        break;
                    }
                }               
            }
            return result;
        }
    }    

}
